# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import os
import time
import re
import subprocess
import shutil 
import hashlib
import threading
import smtplib
import  copy
import logging
from  handleEmail import  HandleEmail
#publish_8910v1.3_at_liangjian@airm2m.com
#publish_1802S_at
#publish_8910v1.3_lua_bt_float
class  mqttRun():
    def __init__(self):
        self.IDLE = 0
        self.START = 1
        self.COMPLATE = 2
        self.RUN = 3
        
        self.state = self.IDLE
        self.email = ""
        self.IsComplat = False
        self.AIRSOURCE = "D:\AirSource\\"
        self.MYPLACE = "D:\AirM2M-Automatic-version-Publishing"
        self.circulateTimeOut = 60
        self.start_circulateTimeOut = 70
        self.circulate_test_req_timer = None
        self.ErrNumber = 50
        self.testTesultEndReq_timer = None
        self.logger = logging.getLogger('mainServer')
        self.logger.setLevel(level=logging.INFO)
        console = logging.StreamHandler()
        formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d line:%(lineno)d %(levelname)s %(message)s', datefmt='%Y-%m-%d %X')
        console.setFormatter(formatter)
        console.setLevel(logging.INFO)
        self.logger.addHandler(console)
        self.HandleEmail = HandleEmail(self.logger)
        #参数1,当前状态 参数2,测试总时长 参数3,开始测试时间 参数4 循环查询定时器handle  参数5 总测试时常定时器
        testMessageDict = {
                "state":self.IDLE,
                "totalTime":0,
                "startTime":0,
                "circulateHandle":0,
                "circulateFlag":0,
                "endHandle":0,
                "email":0,
                "ver":False,
                "Errlog":"",
                "logSize":0,
                "svnRevison":0
                
                 }

        self.TestProcess = {
            "at":copy.deepcopy(testMessageDict),
            "lua":copy.deepcopy(testMessageDict),
            "csdk":copy.deepcopy(testMessageDict)
            }

        self.testTimeTotal = 0
        self.testStartTime = 0

        
    def start_server(self):
        os.system('mosquitto  -c mosquitto.conf')
        

    def compile(self,build ,times =10 ,product = ""):
        for i in range(0,times):
            res = self.execute_command(build,product = product)
            # res = os.system('build')
            self.logger.info(res)
            if res :
                return True
          
        return False
                        
    def getmd5(self,file):
        m = hashlib.md5()
        with open(file,'rb') as f:
            for line in f:
                m.update(line)
        md5code = m.hexdigest()
        self.logger.info(md5code)
        return md5code

    def update_and_compile(self,product,project):
        self.logger.info("start update svn")
        self.IsComplat = True
        compiletSussFlag = False
        res , needCompile = self.update_svn(product,project)
        if res:
            os.chdir(self.AIRSOURCE + product)
            if product.find("8910") != -1:
                self.logger.info("start remove out")
                if os.path.exists(self.AIRSOURCE + product + "\hex"):
                    for root, dirs , files in os.walk(self.AIRSOURCE + product + "\hex"):
                        for file in dirs:
                            full_path = os.path.join(root, file)
                            if file.lower().find(project) != -1:
                                shutil.rmtree(full_path)
                if needCompile:
                    os.system('clean')          
                    pass   
                if project == "at":
                    self.logger.info("start complets")
                    if self.compile("build release",product = product):
                        self.logger.info("update_and_compile succ !!!")
                        compiletSussFlag = True
                    else:
                         self.logger.error("update_and_compile false !!!")                        
                else:
                    if self.compile("build_%s.bat release"%(str(project)),product = product):
                        self.logger.info("update_and_compile succ !!!")
                        compiletSussFlag = True
                    else:
                         self.logger.error("update_and_compile false !!!")
        else:
            self.logger.error("update false !!!")

        self.IsComplat = False    
        time.sleep(2)
        if compiletSussFlag :
            filePath = self.GetPacFile(project)
            os.chdir(self.MYPLACE)
            if filePath :
                path= self.AIRSOURCE + product + "//" + filePath
                return path
            else:
                return False
        else:
            os.chdir(self.MYPLACE)
            self.on_publish("airm2m/testInfo%s"%project,"编译失败，请检查代码".encode("gbk"),1)
            self.state  = self.IDLE
            return False

    def  splite_mes(self,mes):
        mat = re.match("(.*?)_(.*)_email_(.*)_time_(.*)",mes)
        if mat :
            return mat.group(1),mat.group(2),mat.group(3),mat.group(4)
        return False,False,False,False

    def time_timeout(self,project):
        if self.TestProcess[project]["state"] == self.RUN :
            self.on_publish("airm2m/testTesultEndReq%s"%(project),1,1)
        else:
            self.HandleEmail.SendTestEmail(self.TestProcess[project]["email"],"测试异常!!!\r\n%s\r\n"%project)




    def circulate_test_req(self,project,timeout):
        if self.TestProcess[project]["circulateFlag"] == 1:
            self.endTest(project)
            self.HandleEmail.SendTestEmail(self.TestProcess[project]["email"],"测试电脑异常!!!\r\n%s\r\n"%project)
            return
        self.on_publish("airm2m/testcirculateReq%s"%(project),1,1)
        self.TestProcess[project]["circulateHandle"] = threading.Timer(timeout,self.circulate_test_req,args=(project,timeout))
        self.TestProcess[project]["circulateHandle"].start()
        self.TestProcess[project]["circulateFlag"] = 1

    def updateAndComplatThread(self,product,project,timeout):
        res = self.update_and_compile(product,project)
        if res :
            MMM = re.match(".*\\\(.*).pac",res)
            if MMM :
                hd = open(res,"rb")
                data = hd.read()
                hd.close()
                self.TestProcess[project]["ver"] = MMM.group(1)
                self.on_publish("airm2m/UpgradeAndTesVer%s"%project,self.TestProcess[project]["ver"],1)
                time.sleep(2)
                self.on_publish("airm2m/UpgradeAndTestMD5%s"%project,self.getmd5(res),1)
                
                self.on_publish("airm2m/UpgradeAndTest%s"%project,data,1)
                self.TestProcess[project]["state"] = self.COMPLATE
                timeout = timeout*60
                self.TestProcess[project]["endHandle"] = threading.Timer(int(timeout),self.time_timeout,args=(project,))
                self.TestProcess[project]["endHandle"].start()
                
                self.TestProcess[project]["circulateHandle"] = threading.Timer(self.start_circulateTimeOut,self.circulate_test_req,args=(project,self.circulateTimeOut,))
                self.TestProcess[project]["circulateHandle"].start()
                self.TestProcess[project]["totalTime"] = int(timeout)
                self.TestProcess[project]["startTime"] = time.perf_counter()

                self.on_publish("airm2m/testInfo%s"%project,"编译完成，已经发布给测试电脑".encode("gbk"),1)
                self.TestProcess[project]["state"] = self.RUN
            else:
                self.on_publish("airm2m/testInfo%s"%project,"未获取到版本,请查看".encode("gbk"),1)
        else:
            self.logger.error("publish err")
            self.TestProcess[project]["state"] = self.IDLE
            self.HandleEmail.SendTestEmail(self.TestProcess[project]["email"],"SVN 更新或 编译异常!!!")

    def endTest(self,project):
        self.TestProcess[project]["circulateHandle"].cancel()
        self.TestProcess[project]["endHandle"].cancel()
        self.TestProcess[project]["circulateFlag"] = 0
        self.TestProcess[project]["state"] = self.IDLE
        pass    

    def analysis_recv_log(self,paylaod,project):
        self.logger.info("analysis_recv_log")
        tmepMatch = re.match("(.*)_split#(.*)_split#(.*)",paylaod)
        if(tmepMatch):
            
            unitGroup = re.match(".*log/(.*)//",tmepMatch.group(1))
            if unitGroup :
                uint = unitGroup.group(1)
                self.logger.info("path:%s totalsize:%s %s"%(str(tmepMatch.group(1)),str(tmepMatch.group(2)),uint))
                if project == "at":
                    if tmepMatch.group(3).count(": E") > self.ErrNumber:
                        return False,uint,int(tmepMatch.group(2)),str(tmepMatch.group(3))
                        # self.SendTestEmail(self.TestProcess[project]["email"],"TEST FAIL!!!\r\n%s\r\n%s"%(tmepMatch.group(1),tmepMatch.group(3)))
                elif project == "lua":
                    if tmepMatch.group(3).count("[E]-") > self.ErrNumber :
                        return False,uint,int(tmepMatch.group(2)),str(tmepMatch.group(3))
                
                return True,uint,int(tmepMatch.group(2)),str(tmepMatch.group(3))
        return False,False,False,False
                    

    def on_message_come(self,lient, userdata, msg):
        self.logger.info("topic:%s len:%d"%(msg.topic , len(msg.payload)))
        try:
            payload =  str(msg.payload, encoding = 'gbk')
        except Exception as  e:
            self.logger.warning("encoding gbk err")
            payload = str(msg.payload)
        if msg.topic == "airm2m/publish":                                     #测试请求
            product,project,email,timeout  =  self.splite_mes(payload)
            self.logger.info("%s %s %s %s %s"%(product,project,email,timeout,self.TestProcess[project]["state"]))
            if product == "" or project == "" or email == "" or timeout == "":
                return
            
            if self.IsComplat :
                self.on_publish("airm2m/testInfo%s"%project,"有其他任务正在编译，请稍后".encode("gbk"),1)
                return

            if  self.TestProcess[project]["state"]  ==  self.IDLE :
                self.TestProcess[project]["state"] = self.START
                self.TestProcess[project]["email"] = email
                self.on_publish("airm2m/testInfo%s"%project,"当前任务空闲,正在编译".encode("gbk"),1)

                self.executeTest = threading.Thread(target=self.updateAndComplatThread, args=(product,project,int(timeout),))
                self.executeTest.start()
              
            else:
                UsedTime = time.perf_counter() - self.TestProcess[project]["startTime"]
                residueTime = self.TestProcess[project]["totalTime"] - UsedTime
                self.logger.info("%s %s %s"%(str(time.perf_counter()),str(self.TestProcess[project]["startTime"]),str(self.TestProcess[project]["totalTime"])))
                if residueTime > 0 :
                    self.on_publish("airm2m/testInfo%s"%project,("上个版本没有测试完成,剩余时间:%d分钟"%(int(residueTime)/60)).encode("gbk"),1)
                else:
                    self.on_publish("airm2m/testInfo%s"%project,"上个版本没有测试完成,剩余时间 不确定".encode("gbk"),1)
            
        elif msg.topic == "airm2m/testTesultEndCnfat" or msg.topic == "airm2m/testTesultEndCnflua" or msg.topic == "airm2m/testTesultEndCnfcsdk" :  #测试结束返回
            projectMatch = re.match("airm2m/testTesultEndCnf(.*)",msg.topic)
            if projectMatch:
                self.endTest(projectMatch.group(1))
                res,path,logSize,data =  self.analysis_recv_log(payload,projectMatch.group(1))
                if res :
                    self.HandleEmail.SendTestEmail(self.TestProcess[project]["email"],"测试成功!!!\r\n%s\r\n"%(path))
                else:
                    self.HandleEmail.SendTestEmail(self.TestProcess[project]["email"],"测试失败!!!\r\n%s\r\n%s"%(path,data))



        elif msg.topic == "airm2m/testcirculateCnfat" or msg.topic == "airm2m/testcirculateCnflua" or msg.topic == "airm2m/testcirculateCnfcsdk" :  #循环测试返回
            projectMatch = re.match("airm2m/testcirculateCnf(.*)",msg.topic)
            if projectMatch:
                res,path,logSize,data =  self.analysis_recv_log(payload,projectMatch.group(1))
                self.logger.info("res:%s path:%s logsize:%s"%(str(res),str(path),str(logSize)))
                self.TestProcess[projectMatch.group(1)]["circulateFlag"] = 0
                if logSize > self.TestProcess[projectMatch.group(1)]["logSize"]:
                    self.TestProcess[projectMatch.group(1)]["logSize"] = logSize
                    if res:
                        self.logger.info("testcirculateCnf  ok ")
                        self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"轮询检查成功".encode("gbk"),1)
                    else:
                        self.logger.warning("testcirculateCnf  err ")
                        if self.TestProcess[projectMatch.group(1)]["Errlog"] != data :
                            self.TestProcess[projectMatch.group(1)]["Errlog"] = data
                            self.HandleEmail.SendTestEmail(self.TestProcess[projectMatch.group(1)]["email"].encode("gbk"),data,att=True)
                            self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"轮询检查失败，已发邮件".encode("gbk"),1)

                else:
                    self.logger.warning("testcirculateCnf  logSize err test end ")
                    self.HandleEmail.SendTestEmail(self.TestProcess[projectMatch.group(1)]["email"],"测试电脑 日志没有增长，测试异常，需要确认是否死机!!!\r\n%s\r\n"%(path))
                    self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),("测试电脑 日志没有增长，测试异常，需要确认是否死机%s"%(path)).encode("gbk"),1)
                    self.on_publish("airm2m/StopReq%s"%(projectMatch.group(1)),None,1)

            
            
        elif msg.topic == "airm2m/UpgradeAndTestCnfat" or msg.topic == "airm2m/UpgradeAndTestCnflua" or msg.topic == "airm2m/UpgradeAndTestCnfcsdk":   #挂测开始回复
            projectMatch = re.match("airm2m/UpgradeAndTestCnf(.*)",msg.topic)
            if projectMatch:
                if payload.find("0") != -1:
                    self.logger.info("start test succes %s"%projectMatch.group(1))
                    self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"测试PC 已经开始测试".encode("gbk"),1)
                    self.TestProcess[projectMatch.group(1)]["state"] = self.RUN
                else:
                    self.logger.error("start test fail %s"%projectMatch.group(1))
                    self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"测试PC 开始测试失败,请查看原因".encode("gbk"),1)
                    self.TestProcess[projectMatch.group(1)]["state"] = self.IDLE
                
        elif msg.topic == "airm2m/testStopCnfat" or msg.topic == "airm2m/testStopCnflua" or msg.topic == "airm2m/testStopCnfcsdk":   #停止挂测回复
            projectMatch = re.match("airm2m/testStopCnf(.*)",msg.topic)
            if projectMatch:
                if payload.find("0") != -1:
                    self.logger.info("start test succes %s"%projectMatch.group(1))
                    self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"测试PC 停止测试成功".encode("gbk"),1)
                    self.endTest(projectMatch.group(1))
                else:
                    self.on_publish("airm2m/testInfo%s"%(projectMatch.group(1)),"测试PC 停止测试失败".encode("gbk"),1)

            


    def on_subscribe(self):
        self.mqttClient.subscribe("airm2m/publish", 1)

        self.mqttClient.subscribe("airm2m/testTesultEndCnfat", 1)
        self.mqttClient.subscribe("airm2m/testTesultEndCnflua", 1)
        self.mqttClient.subscribe("airm2m/testTesultEndCnfcsdk", 1)
        
        self.mqttClient.subscribe("airm2m/testcirculateCnfat", 1)
        self.mqttClient.subscribe("airm2m/testcirculateCnflua", 1)
        self.mqttClient.subscribe("airm2m/testcirculateCnfcsdk", 1)

        self.mqttClient.subscribe("airm2m/UpgradeAndTestCnfat", 1)
        self.mqttClient.subscribe("airm2m/UpgradeAndTestCnflua", 1)
        self.mqttClient.subscribe("airm2m/UpgradeAndTestCnfcsdk", 1)

        self.mqttClient.subscribe("airm2m/testStopCnfat", 1)
        self.mqttClient.subscribe("airm2m/testStopCnflua", 1)
        self.mqttClient.subscribe("airm2m/testStopCnfcsdk", 1)


        self.mqttClient.on_message = self.on_message_come  

    
    def exceute_timeout(self):
        self.logger.warning("complit timeout")
        self.executecommand.kill()
        pass


    def execute_command(self, cmd,sucess = False,timeout = 10*60 ,product = ""):
        self.logger.info('start executing cmd...')
     
        self.executecommand = subprocess.Popen(str(cmd), shell=True, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
                                               stdout=subprocess.PIPE)
        timer = threading.Timer(timeout,self.exceute_timeout)
        timer.start()
        line = self.executecommand.stdout.readline()

        while line:
            line = self.executecommand.stdout.readline()
            if line :
                try:
                    line =  str(line, encoding = "gbk")
                except Exception as e:
                    line = str(line)
                if sucess and line.find(sucess) != -1:
                    timer.cancel()
                    self.executecommand.kill()
                    self.executecommand = False
                    return line

                if product.find("8910") != -1:    
                    MMM = re.match("\[(\d+)/(\d+)\]",line)
                    if MMM :
                        self.logger.info("%s %s"%(MMM.group(1),MMM.group(2)))
                        if MMM.group(1) == MMM.group(2):
                            timer.cancel()
                            self.executecommand.kill()
                            self.executecommand = False
                            return True
                
            else:
                break
        timer.cancel()
        self.executecommand.kill()
        self.executecommand = False
        return False

    def startMqtt(self):
        MQTTHOST = "127.0.0.1"
        MQTTPORT = 1883
        self.mqttClient = mqtt.Client()
        self.mqttClient.connect(MQTTHOST, MQTTPORT, 60)
        self.mqttClient.loop_start()
        pass

    def update_svn(self,product,project):
        try:
            old_svn = self.TestProcess[project]["svnRevison"]
            if product == "8910v1.2" :
                os.chdir(self.AIRSOURCE+"8910v1.2")
                self.TestProcess[project]["svnRevison"] = self.execute_command('svn update',sucess ="revision")
                os.chdir(self.AIRSOURCE+"8910v1.2\components\\at\src")
                self.TestProcess[project]["svnRevison"] = self.TestProcess[project]["svnRevison"] + self.execute_command('svn update',sucess ="revision")
                os.chdir(self.MYPLACE)
                
            elif product == "8910v1.3" :
                os.chdir(self.AIRSOURCE+"8910v1.3")
                self.TestProcess[project]["svnRevison"] = self.execute_command('svn update',sucess = "revision")
                os.chdir(self.AIRSOURCE+"8910v1.3\components\\at\src")
                self.TestProcess[project]["svnRevison"] = self.TestProcess[project]["svnRevison"] + self.execute_command('svn update',sucess = "revision")
                os.chdir(self.MYPLACE)
            self.logger.info("update svn old:%s new:%s"%(old_svn,self.TestProcess[project]["svnRevison"]))
            return True,(old_svn != self.TestProcess[project]["svnRevison"])
        except Exception as  E:
            self.logger.info("update err %s"%E)
            return False,False

    def GetPacFile(self,project):
        self.logger.info("find pac")
        fileList = os.listdir("hex")
        for i in fileList:
            if i.find("zip") == -1 and i.find("MAP") == -1:
                fileList = os.listdir("hex"+"\\"+i)
                for j in fileList:
                    if j.find("pac") != -1 and j.lower().find(project.lower()) != -1  :
                        return "hex" + "\\" + i + "\\" + j
        return False

    def on_publish(self,topic,mes,qos):
        self.logger.info("on_publish %s"%(str(topic)))
        self.mqttClient.publish(topic, mes, qos)

        pass

    def run(self):
        self.start_server()
        self.startMqtt()
        self.on_subscribe()
        while(True):
            time.sleep(30)
        pass


if __name__ == '__main__':
    handle = mqttRun()
    handle.run()
